﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Reflection;

namespace RaisingStudio.Data
{
    /// <summary>
    /// The attribute of definition name.
    /// </summary>
    [Serializable]
    public class DefinitionNameAttribute : Attribute
    {
        #region static member
        public const string FILENAME = "definition.xml";
        public const string DEFINITION_MARK = "Definition";

        public const string DATATABLE_MARK = "DataTable";
        public const string TABLENAME_MARK = "TableName";
        public const string DATASET_MARK = "DataSet";
        public const string DATASETNAME_MARK = "DataSetName";


        /// <summary>
        /// Load the definition xml file.
        /// </summary>
        /// <param name="type">Type.</param>
        /// <param name="fileName">file name.</param>
        /// <param name="name">name.</param>
        /// <param name="fullName">Indicates whether it is  full name.</param>
        /// <returns>xml element.</returns>
        public static System.Xml.XmlElement LoadXmlDocument(System.Type type, string fileName, string name, bool fullName)
        {
            System.IO.Stream stream;
            if (fullName)
            {
                stream = type.Assembly.GetManifestResourceStream(fileName);
            }
            else
            {
                stream = type.Assembly.GetManifestResourceStream(type, fileName);
            }
            if (stream == null)
            {
                // try to read all related assemblies.
                stream = GetEmbeddedResource(fileName);
            }
            if (stream == null)
            {
                // try to read the type embedded resource.
                stream = type.Assembly.GetManifestResourceStream(type.FullName);
            }
            if (stream != null)
            {
                System.Xml.XmlDocument xmlDocument = new System.Xml.XmlDocument();
                xmlDocument.Load(stream);
                System.Xml.XmlElement xmlElement = LoadXmlDocument(name, xmlDocument);
                return xmlElement;
            }
            else
            {
                return null;
            }
        }

        #region Inner Class : FileAssemblyInfo
        /// <summary>
        /// Holds data about a <see cref="System.Type"/> and it's
        /// attendant <see cref="System.Reflection.Assembly"/>.
        /// </summary>
        internal class FileAssemblyInfo
        {
            #region Constants
            /// <summary>
            /// The string that separates file name
            /// from their attendant <see cref="System.Reflection.Assembly"/>
            /// names in an assembly qualified type name.
            /// </summary>
            public const string FileAssemblySeparator = ",";
            #endregion

            #region Fields
            private string _unresolvedAssemblyName = string.Empty;
            private string _unresolvedFileName = string.Empty;
            private string _originalFileName = string.Empty;
            #endregion

            #region Properties

            /// <summary>
            /// The resource file name .
            /// </summary>
            public string ResourceFileName
            {
                get { return AssemblyName + "." + FileName; }
            }

            /// <summary>
            /// The original name.
            /// </summary>
            public string OriginalFileName
            {
                get { return _originalFileName; }
            }

            /// <summary>
            /// The file name portion.
            /// </summary>
            public string FileName
            {
                get { return _unresolvedFileName; }
            }

            /// <summary>
            /// The (unresolved, possibly partial) name of the attandant assembly.
            /// </summary>
            public string AssemblyName
            {
                get { return _unresolvedAssemblyName; }
            }

            /// <summary>
            /// Is the type name being resolved assembly qualified?
            /// </summary>
            public bool IsAssemblyQualified
            {
                get
                {
                    if (AssemblyName == null || AssemblyName.Trim().Length == 0)
                    {
                        return false;
                    }
                    else
                    {
                        return true;
                    }
                }
            }

            #endregion

            #region Constructor (s) / Destructor
            /// <summary>
            /// Creates a new instance of the FileAssemblyInfo class.
            /// </summary>
            /// <param name="unresolvedFileName">
            /// The unresolved name of a <see cref="System.Type"/>.
            /// </param>
            public FileAssemblyInfo(string unresolvedFileName)
            {
                SplitFileAndAssemblyNames(unresolvedFileName);
            }
            #endregion

            #region Methods
            /// <summary>
            /// 
            /// </summary>
            /// <param name="originalFileName"></param>
            private void SplitFileAndAssemblyNames(string originalFileName)
            {
                _originalFileName = originalFileName;

                int separatorIndex = originalFileName.IndexOf(FileAssemblyInfo.FileAssemblySeparator);

                if (separatorIndex < 0)
                {
                    _unresolvedFileName = originalFileName.Trim();
                    _unresolvedAssemblyName = null; // IsAssemblyQualified will return false
                }
                else
                {
                    _unresolvedFileName = originalFileName.Substring(0, separatorIndex).Trim();
                    _unresolvedAssemblyName = originalFileName.Substring(separatorIndex + 1).Trim();
                }
            }
            #endregion
        }
        #endregion

        public static Stream GetEmbeddedResource(string resource)
        {
            Stream stream = null;
            try
            {
                FileAssemblyInfo fileInfo = new FileAssemblyInfo(resource);
                if (fileInfo.IsAssemblyQualified)
                {
                    Assembly assembly = null;
                    assembly = Assembly.Load(fileInfo.AssemblyName);
                    stream = assembly.GetManifestResourceStream(fileInfo.ResourceFileName);
                    if (stream == null)
                    {
                        stream = assembly.GetManifestResourceStream(fileInfo.FileName);
                    }
                }
                else
                {
#if (PocketPC || Smartphone || WindowsCE)
#else
                    // bare type name... loop thru all loaded assemblies
                    Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
                    foreach (Assembly assembly in assemblies)
                    {
                        // Dynamic assembly.
                        if (!(assembly is System.Reflection.Emit.AssemblyBuilder))
                        {
                            stream = assembly.GetManifestResourceStream(fileInfo.FileName);
                        }
                    }
#endif
                }
            }
            catch (Exception ex)
            {
                // Nothing to do.
            }
            return stream;
        }

        public static System.Xml.XmlElement LoadXmlDocument(string name, System.Xml.XmlDocument xmlDocument)
        {
            System.Xml.XmlElement documentElement = xmlDocument.DocumentElement;
            System.Xml.XmlElement xmlElement = null;
            if (documentElement.Name == DEFINITION_MARK)
            {
                if (documentElement.HasChildNodes)
                {
                    if (documentElement.ChildNodes.Count > 1)
                    {
                        foreach (System.Xml.XmlNode childXmlNode in documentElement)
                        {
                            if (childXmlNode is System.Xml.XmlElement)
                            {
                                System.Xml.XmlElement childElement = childXmlNode as System.Xml.XmlElement;
                                if ((childElement.Name == DATATABLE_MARK) && (childElement.Attributes[TABLENAME_MARK].Value == name))
                                {
                                    xmlElement = childElement;
                                    break;
                                }
                                else if ((childElement.Name == DATASET_MARK) && (childElement.Attributes[DATASETNAME_MARK].Value == name))
                                {
                                    xmlElement = childElement;
                                    break;
                                }
                            }
                        }
                    }
                    else
                    {
                        if (documentElement.ChildNodes[0] is System.Xml.XmlElement)
                        {
                            xmlElement = documentElement.ChildNodes[0] as System.Xml.XmlElement;
                        }
                        else
                        {
                            throw (new System.ArgumentException(string.Format("文件中Not has 任何definition.")));
                        }
                    }
                }
                else
                {
                    throw (new System.ArgumentException(string.Format("文件中Not has 任何definition.")));
                }
            }
            else
            {
                throw (new System.ArgumentException(string.Format("文件格式Not 正确。")));
            }
            return xmlElement;
        }

        /// <summary>
        /// Load the definition xml file.
        /// </summary>
        /// <param name="type">Type.</param>
        /// <param name="name">name.</param>
        /// <param name="fullName">Indicates whether it is  full name.</param>
        /// <returns>xml element.</returns>
        private static System.Xml.XmlElement LoadXmlDocument(System.Type type, string name, bool fullName)
        {
            string fileName = string.Format("{0}.{1}", name, FILENAME);
            return LoadXmlDocument(type, fileName, name, fullName);
        }

        /// <summary>
        /// Load the definition xml file.
        /// </summary>
        /// <param name="type">Type.</param>
        /// <param name="name">name.</param>
        /// <returns>xml element.</returns>
        public static System.Xml.XmlElement LoadXmlDocument(System.Type type, string name)
        {
            return LoadXmlDocument(type, name, false);
        }

        /// <summary>
        /// Load the definition xml file.
        /// </summary>
        /// <param name="type">Type.</param>
        /// <returns>xml element.</returns>
        public static System.Xml.XmlElement LoadXmlDocument(System.Type type)
        {
            DefinitionNameAttribute definitionNameAttribute = Attribute.GetCustomAttribute(type, typeof(DefinitionNameAttribute)) as DefinitionNameAttribute;
            if (definitionNameAttribute != null)
            {
                if (definitionNameAttribute.FileName != null)
                {
                    string fileName = definitionNameAttribute.FileName;
                    string name = definitionNameAttribute.Name;
                    return DefinitionNameAttribute.LoadXmlDocument(type, fileName, name, false);
                }
                else
                {
                    string name = definitionNameAttribute.Name;
                    return DefinitionNameAttribute.LoadXmlDocument(type, name, false);
                }
            }
            else
            {
                string name = type.FullName;
                return DefinitionNameAttribute.LoadXmlDocument(type, name, true);
            }
        }
        #endregion

        private string name = string.Empty;
        /// <summary>
        /// name.
        /// </summary>
        public string Name
        {
            get
            {
                return this.name;
            }
            set
            {
                this.name = value;
            }
        }

        private string fileName;
        /// <summary>
        /// file name.
        /// </summary>
        public string FileName
        {
            get
            {
                return this.fileName;
            }
            set
            {
                this.fileName = value;
            }
        }

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="name">name.</param>
        public DefinitionNameAttribute(string name)
        {
            this.name = name;
        }

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="fileName">file name.</param>
        /// <param name="name">name.</param>
        public DefinitionNameAttribute(string fileName, string name)
        {
            this.fileName = fileName;
            this.name = name;
        }
    }
}
